package log

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/lichuanning/chanwind-kit/v1/service/dump"
	sls "github.com/aliyun/aliyun-log-go-sdk"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/gogo/protobuf/proto"
	"time"
)

// defaultBusinessSLSKeys 默认的业务 SLS 日志 key
//var defaultBusinessSLSKeys = []string{
//	"msg_header",
//	"msg_body",
//	"token",
//}

type ChanWindSLS interface {
	// 业务日志
	Error(ctx context.Context, logPath, msgHeader, token, env string, args g.Map) error              // 业务日志：ERROR
	Info(ctx context.Context, logPath, msgHeader, token, env string, args g.Map) error               // 业务日志：INFO
	ErrorWithCustomFields(ctx context.Context, logPath string, logInfo map[string]interface{}) error // TODO 配合 SlSKeyArrays使用
	// 请求日志
	RequestI
}

// RequestI 请求
type RequestI interface {
	RR(ctx context.Context, requestInfo *HttpRequestInfoLog) error // 请求上报 request report
}

// SLSObject qf 日志对象
type SLSObject struct {
	SlSClient               sls.ClientInterface // Aliyun SLS client
	ProjectName             string              // 项目名称
	OperationalLogStoreName string              // 日常业务日志名称
	RequestLogStoreName     string              // 日常请求日志名称
}

// cWLog 日志
type cWLog struct {
	AliyunSlSClient *SLSObject // Aliyun SLS client
	SlSKeyArrays    []string
}

// NewCWLog 创建日志对象
// @param client qf日志对象
// @param keys 日志 key 列表，用于自定义日志 key
func NewCWLog(client *SLSObject, businessKeys ...string) ChanWindSLS {
	//var slsKeyArrays = defaultBusinessSLSKeys
	//if len(keys) > 0 {
	//	slsKeyArrays = businessKeys
	//}
	return &cWLog{
		AliyunSlSClient: client,
		//SlSKeyArrays:    slsKeyArrays,
	}
}

// HttpRequestInfoLog is for JSON marshaling in sequence.
type HttpRequestInfoLog struct {
	RequestId  string                 `json:"requestId"`
	Type       int                    `json:"type"`
	Token      string                 `json:"token"`
	Time       string                 `json:"time"`
	Url        string                 `json:"url"`
	Method     string                 `json:"method"`
	Ip         string                 `json:"ip"`
	Headers    map[string]string      `json:"headers"`
	Env        string                 `json:"env"`
	PostParams string                 `json:"postParams"`
	GetParams  map[string]interface{} `json:"getParams"`
}

// Error 业务日志：ERROR logPath为空不写入本地日志文件
func (l *cWLog) Error(ctx context.Context, logPath, msgHeader, token, env string, args g.Map) error {
	errorInfo := g.Map{
		"token":       token,
		"msg_header":  msgHeader,
		"msg_body":    args,
		"env":         env,
		"error_stack": glog.GetStack(1), // 堆栈信息
	}
	if logPath != "" {
		glog.Cat(logPath).File("error-{Ymd}.log").
			Stack(true).
			Skip(1).Error(ctx, errorInfo)
	}
	return l.writeToSLS(errorInfo, l.AliyunSlSClient.OperationalLogStoreName, "Error")
}

// Info 业务日志：INFO logPath为空不写入本地日志文件
func (l *cWLog) Info(ctx context.Context, logPath string, msgHeader, token, env string, args g.Map) error {
	info := g.Map{
		"msg_header": msgHeader,
		"msg_body":   args,
		"env":        env,
		"token":      token,
	}
	if logPath != "" {
		glog.Cat(logPath).File("info-{Ymd}.log").Info(ctx, info)
	}
	return l.writeToSLS(info, l.AliyunSlSClient.OperationalLogStoreName, "Info")
}

// RR 请求上报 request report
func (l *cWLog) RR(ctx context.Context, requestInfo *HttpRequestInfoLog) error {
	gMapData := g.Map{}
	err := gconv.Struct(requestInfo, &gMapData)
	if err != nil {
		return err
	}
	return l.writeToSLS(gMapData, l.AliyunSlSClient.RequestLogStoreName, "ReportRequest")
}

// writeToSLS 写入 SLS
func (l *cWLog) writeToSLS(logMap g.Map, LogStoreName, topicType string) error {
	if logMap == nil {
		return errors.New("log map is nil")
	}
	// 日志 Content
	var content []*sls.LogContent
	for logKey, logVal := range logMap {
		var valString string

		switch logVal.(type) {
		case string:
			valString = logVal.(string)
		case int:
			valString = fmt.Sprintf("%d", logVal.(int))
		default:
			marshalString, err := json.Marshal(logVal)
			if err != nil {
				valString = dump.GetCompositeString(logVal)
			} else {
				valString = string(marshalString)
			}
		}
		content = append(content, &sls.LogContent{
			Key:   proto.String(logKey),
			Value: proto.String(valString),
		})
	}
	//  日志
	writeLog := &sls.Log{
		Time:     proto.Uint32(uint32(time.Now().Unix())),
		Contents: content,
	}
	// 写入日志组
	// TODO 可以考虑写入 MQ 通过消费者在聚合分组，从而避免一个日志一个组
	loggroup := &sls.LogGroup{
		Topic: proto.String(topicType), // group的分组主题信息，大概就是组字
		//Source: proto.String("123"),
		Logs: []*sls.Log{writeLog},
	}
	client := l.AliyunSlSClient
	// 写入远程 sls
	return client.SlSClient.PutLogs(client.ProjectName, LogStoreName, loggroup)
}

// ErrorWithCustomFields 自定义日志
func (l *cWLog) ErrorWithCustomFields(ctx context.Context, logPath string, logInfo map[string]interface{}) error {
	//TODO implement me
	return errors.New("implement me")
}
